﻿namespace BizTalk.ESB.Extensions.ItineraryOM
{
    using BTS;
    using Microsoft.BizTalk.Bam.EventObservation;
    using Microsoft.BizTalk.Component.Interop;
    using Microsoft.BizTalk.Message.Interop;
    using Microsoft.Practices.ESB.Configuration;
    using Microsoft.Practices.ESB.Exception.Management;
    using Microsoft.Practices.ESB.GlobalPropertyContext;
    using Microsoft.Practices.ESB.Itinerary;
    using Microsoft.XLANGs.BaseTypes;
    using System;
    using System.Collections.Generic;
    using System.Configuration;
    using System.Diagnostics;
    using System.Linq;
    using System.Reflection;

    public static class BAMItineraryProcess
    {
        private const string ItineraryServiceActivity = "ItineraryServiceActivity";
        private const string ItineraryActivity = "ItineraryActivity";

        private static object[] GetItineraryDataForBAM(Itinerary itinerary, string interchangeId)
        {
            object[] objArray;
            if (interchangeId == null)
            {
                throw new ArgumentNullException("interchangeId");
            }
            var bamList = new List<string>();
            try
            {
                bamList.Add("InterchangeId");
                bamList.Add(interchangeId);
                bamList.Add("ItineraryBeginTime");
                bamList.Add(itinerary.beginTime);
                if (string.Compare(itinerary.state, "Pending") == 0)
                {
                    bamList.Add("ItineraryEndTime");
                    bamList.Add(null);
                }
                else
                {
                    bamList.Add("ItineraryEndTime");
                    bamList.Add(itinerary.completeTime);
                }
                bamList.Add("ItineraryisRequestResponse");
                bamList.Add(itinerary.isRequestResponse.ToString());
                bamList.Add("ItineraryState");
                bamList.Add(itinerary.state);
                EventLogger.Write("BAMItineraryProcess.GetItineraryDataForBAM(), state = " + itinerary.state);
                bamList.Add("Itineraryuuid");
                bamList.Add(itinerary.uuid);
                bamList.Add("TimeStamp");
                bamList.Add(itinerary.beginTime);
                bamList.Add("ItineraryName");
                bamList.Add(itinerary.name);
                bamList.Add("ItineraryVersion");
                bamList.Add(itinerary.version);
                bamList.Add("GlobalCorrelationId");
                bamList.Add(itinerary.correlationId);
                objArray = bamList.ToArray<object>();
            }
            catch (Exception exception)
            {
                EventLogger.LogMessage(EventFormatter.FormatEvent(MethodBase.GetCurrentMethod(), exception), EventLogEntryType.Error, 0x17ca);
                throw;
            }
            finally
            {
                bamList = null;
            }
            return objArray;
        }

        private static object[] GetItineraryDataForBAM(Itinerary itinerary, IItineraryStep step)
        {
            object[] objArray;
            var bamList = new List<string>();
            try
            {
                bamList.Add("Itineraryuuid");
                bamList.Add(itinerary.uuid);
                bamList.Add("MessageDirection");
                bamList.Add(step.MessageDirection.ToString());
                bamList.Add("ServiceisRequestResponse");
                bamList.Add(itinerary.Services[step.Position].Service.isRequestResponse.ToString());
                bamList.Add("ServicePosition");
                bamList.Add(itinerary.Services[step.Position].Service.position.ToString());
                bamList.Add("ServiceType");
                bamList.Add(itinerary.Services[step.Position].Service.type);
                bamList.Add("ServiceState");
                bamList.Add(itinerary.Services[step.Position].Service.state);
                bamList.Add("Serviceuuid");
                bamList.Add(itinerary.Services[step.Position].Service.uuid);
                bamList.Add("ServiceIdGuid");
                bamList.Add(itinerary.Services[step.Position].Service.serviceInstanceId);
                bamList.Add("TimeStamp");
                bamList.Add(itinerary.Services[step.Position].Service.beginTime);
                bamList.Add("ServiceBusinessName");
                bamList.Add(itinerary.Services[step.Position].Service.businessName);
                bamList.Add("ESBServiceName");
                bamList.Add(itinerary.Services[step.Position].Service.name);
                objArray = bamList.ToArray<object>();
            }
            catch (Exception exception)
            {
                EventLogger.LogMessage(EventFormatter.FormatEvent(MethodBase.GetCurrentMethod(), exception), EventLogEntryType.Error, 0x17ca);
                throw;
            }
            finally
            {
                bamList = null;
            }
            return objArray;
        }

        public static void FinishItineraryBAM(Itinerary itinerary, XLANGMessage message)
        {
            Trace.WriteLine("ItineraryV2 - Ending Itinerary BAM Activity... UUID = " + itinerary.uuid
                + ", ActivityID = " + itinerary.activityId + ", State = " + itinerary.state + ", EndTime = " + itinerary.completeTime);
            try
            {
                if (itinerary.state == State.Complete)
                {
                    string interchangeId = message.GetPropertyValue(typeof(InterchangeID)) as string;
                    OrchestrationEventStream.UpdateActivity(ItineraryActivity, itinerary.activityId,
                        GetItineraryDataForBAM(itinerary, interchangeId));
                    OrchestrationEventStream.EndActivity(ItineraryActivity, itinerary.activityId);
                }
            }
            catch (Exception exception)
            {
                EventLogger.Write(MethodBase.GetCurrentMethod(), exception);
                throw;
            }
        }

        public static void FinishItineraryBAM(Itinerary itinerary, IBaseMessage msg)
        {
            Trace.WriteLine("ItineraryV2 - Ending Itinerary BAM Activity... UUID = " + itinerary.uuid
                + ", ActivityID = " + itinerary.activityId + ", State = " + itinerary.state + ", EndTime = " + itinerary.completeTime);
            try
            {
                if (itinerary.state == State.Complete)
                {
                    string interchangeId = msg.Context.Read(BtsProperties.InterchangeID.Name, BtsProperties.InterchangeID.Namespace) as string;
                    EventStream eventStream;
                    ConnectionStringSettings bAMConnectionSettings = ConfigHelper.GetBAMConnectionSettings();
                    string connectionString = bAMConnectionSettings.ConnectionString;
                    eventStream = new BufferedEventStream(connectionString, 1);
                    eventStream.UpdateActivity(ItineraryActivity, itinerary.activityId,
                        GetItineraryDataForBAM(itinerary, interchangeId));
                    eventStream.EndActivity(ItineraryActivity, itinerary.activityId);
                    eventStream.Flush();
                }
            }
            catch (Exception exception)
            {
                EventLogger.Write(MethodBase.GetCurrentMethod(), exception);
                throw;
            }
        }

        public static void StartItineraryBAM(Itinerary itinerary, IBaseMessage msg)
        {
            string interchangeId = msg.Context.Read(BtsProperties.InterchangeID.Name, BtsProperties.InterchangeID.Namespace) as string;
            //SubmitToBAMFromPipelineContext(itinerary, null, ItineraryActivity, Guid.NewGuid().ToString(), GetItineraryDataForBAM(itinerary, interchangeId, globalCorrelationId));
            Trace.WriteLine("ItineraryV2 - Starting Itinerary BAM Activity... UUID = " + itinerary.uuid
                + ", ActivityID = " + itinerary.activityId);
            try
            {
                EventStream eventStream;
                ConnectionStringSettings bAMConnectionSettings = ConfigHelper.GetBAMConnectionSettings();
                string connectionString = bAMConnectionSettings.ConnectionString;
                eventStream = new BufferedEventStream(connectionString, 1);
                
                eventStream.BeginActivity(ItineraryActivity, itinerary.activityId);
                eventStream.UpdateActivity(ItineraryActivity, itinerary.activityId, GetItineraryDataForBAM(itinerary, interchangeId));
                eventStream.Flush();
                // Don't End the activity yet.  Do that when the last service finishes, as part of the Advance() method
            }
            catch (Exception exception)
            {
                EventLogger.Write(MethodBase.GetCurrentMethod(), exception);
                throw;
            }
        }

        public static void UpdateItineraryBAM(Itinerary itinerary, IBaseMessage msg)
        {
            string interchangeId = msg.Context.Read(BtsProperties.InterchangeID.Name, BtsProperties.InterchangeID.Namespace) as string;
            //SubmitToBAMFromPipelineContext(itinerary, null, ItineraryActivity, Guid.NewGuid().ToString(), GetItineraryDataForBAM(itinerary, interchangeId, globalCorrelationId));
            Trace.WriteLine("ItineraryV2 - Starting Itinerary BAM Activity... UUID = " + itinerary.uuid
                + ", ActivityID = " + itinerary.activityId);
            try
            {
                EventStream eventStream;
                ConnectionStringSettings bAMConnectionSettings = ConfigHelper.GetBAMConnectionSettings();
                string connectionString = bAMConnectionSettings.ConnectionString;
                eventStream = new BufferedEventStream(connectionString, 1);

                eventStream.UpdateActivity(ItineraryActivity, itinerary.activityId, GetItineraryDataForBAM(itinerary, interchangeId));
                eventStream.Flush();
                // Don't End the activity yet.  Do that when the last service finishes, as part of the Advance() method
            }
            catch (Exception exception)
            {
                EventLogger.Write(MethodBase.GetCurrentMethod(), exception);
                throw;
            }
        }

        public static void SubmitToBAM(Itinerary itinerary, IItineraryStep step, XLANGMessage message)
        {
            Trace.WriteLine("ItineraryV2 - Recording Itinerary Service BAM Activity... UUID = " + itinerary.uuid
                + ", Service = " + step.ServiceName + ", ActivityID = " + itinerary.activityId);
            if (itinerary.Services[step.Position].Service.isTrackingEnabled)
            {
                //string propertyValue = message.GetPropertyValue(typeof(InterchangeID)) as string;
                SubmitToBAMFromOrchestration(itinerary, ItineraryServiceActivity, Guid.NewGuid().ToString(), GetItineraryDataForBAM(itinerary, step));
            }
            else
            {
                EventLogger.Write("BAM Tracking is not enabled.");
            }
        }

        public static void SubmitToBAM(Itinerary itinerary, IItineraryStep step, IPipelineContext context, IBaseMessage msg)
        {
            Trace.WriteLine("ItineraryV2 - Recording Itinerary Service BAM Activity... UUID = " + itinerary.uuid
                + ", Service = " + step.ServiceName + ", ActivityID = " + itinerary.activityId);
            if (itinerary.Services[step.Position].Service.isTrackingEnabled)
            {
                //string interchangeId = msg.Context.Read(BtsProperties.InterchangeID.Name, BtsProperties.InterchangeID.Namespace) as string;
                SubmitToBAMFromPipelineContext(itinerary, context, ItineraryServiceActivity, Guid.NewGuid().ToString(), GetItineraryDataForBAM(itinerary, step));
            }
            else
            {
                EventLogger.Write("BAM Tracking is not enabled.");
            }
        }

        private static void SubmitToBAMFromOrchestration(Itinerary itinerary, string processActivityName, string activityIdentifier, object[] bamData)
        {
            try
            {
                OrchestrationEventStream.BeginActivity(processActivityName, activityIdentifier);
                OrchestrationEventStream.UpdateActivity(processActivityName, activityIdentifier, bamData);
                OrchestrationEventStream.EndActivity(processActivityName, activityIdentifier);

                // Link ItineraryService activity to Itinerary Activity
                OrchestrationEventStream.AddRelatedActivity(ItineraryActivity, itinerary.activityId, processActivityName, activityIdentifier);
            }
            catch (Exception exception)
            {
                EventLogger.Write(MethodBase.GetCurrentMethod(), exception);
                throw;
            }
        }

        private static void SubmitToBAMFromPipelineContext(Itinerary itinerary, IPipelineContext pContext, string processActivityName, string activityIdentifier, object[] bamData)
        {
            try
            {
                EventStream eventStream;
                ConnectionStringSettings bAMConnectionSettings = ConfigHelper.GetBAMConnectionSettings();
                string connectionString = bAMConnectionSettings.ConnectionString;
                if (string.Equals(bAMConnectionSettings.ProviderName, "BES") || pContext == null)
                {
                    eventStream = new BufferedEventStream(connectionString, 1);
                }
                else
                {
                    eventStream = pContext.GetEventStream();
                }
                eventStream.BeginActivity(processActivityName, activityIdentifier);
                eventStream.UpdateActivity(processActivityName, activityIdentifier, bamData);
                eventStream.EndActivity(processActivityName, activityIdentifier);

                // Link ItineraryService activity to Itinerary Activity
                //eventStream.AddRelatedActivity(ItineraryActivity, itinerary.activityId, processActivityName, activityIdentifier);

                eventStream.Flush();
            }
            catch (Exception exception)
            {
                EventLogger.Write(MethodBase.GetCurrentMethod(), exception);
                throw;
            }
        }
    }
}

